unit uEngVSC;

interface

uses
  Windows, SysUtils, Classes;

const
  FEATENB = 1;
  FEATDIS = 2;
  //
  SET_FEATURE = 1;
  RD_FEATURE = 2;
  //
  RSVD_LOGICAL_HEADS = 8; // Logical heads in Reserved area.
  RSVD_LOGICAL_SPT = 4096; // Logical SPT in Reserved area.
  //
  // VSC Control and Data Pass Thru Key Defines
  SMART_CTRLKEY = $BE; // VSC Control pass-thru key
  SMART_DATAKEY = $BF; // VSC Data pass-thru key

  DEFAULT_RSV_FILE_WBFR_SIZE = 8192 * 512;

const
  // ===================================================================
  // VSC ACTION CODES
  // ===================================================================
  AC_RDWRResFile = 8; // Read/Write Resident file
  AC_CHKFILEID = 9; // Check Reserved resident file
  AC_RDWRMTBL = 11; // Read/write memory table
  AC_RDWRCHS = 12; // Read/Write CHS segment
  AC_GETDRVTBL = 13; // Get Drive Data Tables
  AC_FEATCTRL = 14; // Feature Control Command
  AC_ERATETBL = 15; // Read/Reset Error Rate Table
  AC_RDWRWEDGE = 17; // Read/Write Wedge
  AC_VIRTSK = 18; // Virtual Seek to Track
  AC_RDWRMEM = 19; // Read/Write Memory Location
  AC_RDWRBUFEXT = 20; // Read/Write Buffer Extended
  AC_LBATOCHS = 21; // Translate logical lba to chs address
  AC_CHSTOLBA = 22; // Translate physical CHS to LBA address
  AC_SERVOCTRL = 23; // Servo Control Functions
  AC_FMTUNIT = 24; // Format Unit
  AC_ExecFile = 25; // Execute File
  AC_RDWRLONG = 26; // Read/Write Long
  AC_SECTCTRL = 27; // Sector Control
  AC_RESET = 28; // Initiate power on reset
  AC_RDWRVFYLBA = 29; // Read/Write/Verify LBA segment
  AC_WRALLWITHIDS = 30; // Write All with ID's
  AC_ExpCtrl = 31; // Exception control
  AC_TRKOFT = 32; // Set Track offset
  AC_RDWRDEV = 33; // Read/write device
  AC_ERR_INJ = 34; // Error Injection
  AC_LOGSK = 35; // Logical Seek
  AC_RWERASEFLASH = 36; // Read/Write/Erase Flash
  AC_WRITETRK = 37; // Write Track
  AC_RDWRTPI = 38; // Read/Write TPI Parameters
  AC_HDDEPOPCTRL = 39; // Head Depop Control
  AC_FileMgr = 40; // File System Magnager
  AC_PSTTEST = 41; // PST Test Mode
  AC_RDWRFIELD = 42; // Read/Write Field
  AC_CHWB2PSN = 44; // Translate CHWB to PSN
  AC_EXECDIAG = 46; // Execute a diagnostic (previously downloaded)
  AC_GainCalCntrl = 47; // VSC Gain Cal Control
  AC_FmtSelect = 48; // VSC Format Selection
  AC_RDWRRSVDLBA = 50; // VSC Read/Write Reseved LBA
  AC_RLBATOCHS = 51; // VSC Translate Resrved RLBA to Virtual CHS
  AC_DFHcontrol = 52; // Control of DFH DAC parameters
  AC_PWL = 53; // PWL Monitor Feature Control Command
  AC_XlatTrkAddr = 54; // Translate Track Address
  AC_DriveConfig = 55; // UCCM Drive Configuration

const
  // Defines for Read/Write Memory Table
  mtZone = 0; // Zone Table (Memory Table)
  mtDrvCfg = 1; // Drive Configuration (Memory Table)
  mtChannel = 2; // Read Channel Table (Memory Table)
  mtWrCurr = 3; // Write Current Table (Memory Table)
  mtDRM = 5; // DRM Period Table (Memory Table)
  mtHDA = 6; // HDA Params Table (Memory Table)
  mtDRMOL = 7; // DRM Offline Timers (Memory Table)
  mtMRBias = 9; // MR Bias Table (Memory Table)
  mtMRJog = 10; // MR Jog Table (Memory Table)vmtMRHdLin
  mtMRLin = 11; // MR Head Linearity (Memory Table)
  mtMRHDRes = 12; // MR Head Resistance (Memory Table)
  mtEventLog = 13; // Event Log Table
  mtCacheRelo = 14; // Cache Relocation (Memory Table)
  mtSeekPro = 15; // Seek Profile Performance

const
  // Task File Bit Defines
  RSTAT_BSY = 7;

  // ===================================================================================
  // ENUMS (VSC FUNCTION CODES, FEATURES, ETC)
  // ===================================================================================
type
  // Function code for Read/Write Reserved File (Action code 8)

  eAC_RDWRResFile = ( //
    eRDRESFILE = 1, // 1 Read Reserved File
    eWRRESFILE, // 2 Write Reserved File
    eDNSTATICFILE, // 3 Download from media to static file
    eUPSTATICFILE, // 4 Upload from static file to media
    eCreateFile, // 5 Create file
    eDeleteRemoveFile, // 6 Delete File
    eMarkFileBad, // 7 Mark file Bad, Set ID to "bad" id and don't free space
    eReturnAttribs, // 8 Return file attributes
    eSetAttribs, // 9 set file attributes
    eReadSingleCopy, // 10 Read single copy of file
    eWriteSingleCopy, // 11 Write single copy of file
    eReturnFileInfo, // 12 Return file info
    eReadPartialFile, // 13 Read Partial file
    eWritePartialFile, // 14 Write Partial file
    eReadPartialByte, // 15 Read Partial file
    eWritePartialByte, // 16 Write Partial file
    eReadSinglePartialFile, // 17 Read single partial file without checksum.
    eWriteSinglePartialFile); // 18 Write single partial file without checksum.

  eFile_Attribute = ( //
    eATTR_REGION_0, //
    eATTR_REGION_1, //
    eATTR_REGION_2, //
    eATTR_REGION_3, //
    eATTR_REGION_4, //
    eATTR_REGION_5, //
    eATTR_REGION_6, //
    eATTR_REGION_7, //
    eATTR_FLASH, //
    eATTR_RESERVED0, //
    eATTR_RD_ONLY, //
    eATTR_CHKSUM, //
    eATTR_HEADER, //
    eATTR_PLHOLDER, //
    eATTR_STATICFL, //
    eATTR_PTM, //
    eATTR_PTSM, //
    eATTR_TRANSIENT, //
    eATTR_FILECREATE, //
    eATTR_FILEPRESERVE, //
    eATTR_FIELDCODE, //
    eATTR_LASTAVAILSPACE, //
    eATTR_RESERVED5, //
    eATTR_RESERVED6, //
    eATTR_RESERVED7, //
    eATTR_RESERVED8, //
    eATTR_RESERVED9, //
    eATTR_RESERVED10, //
    eATTR_RESERVED11, //
    eATTR_RESERVED12, //
    eATTR_RESERVED13, //
    eATTR_RESERVED14, //
    eATTR_RESERVED15); //

  // Function code for Read/Write Memory Table (Action code 11)

  eAC_RDWRMTBL = ( //
    eRDMEMTABLE = 1, // Read Memory table
    eWRMEMTABLE, // Write Memory table
    eWRIMDMEMTABLE, // Write Immediate Memory table
    eRDSTATICFILE, // Read Static File
    eWRSTATICFILE, // Write Static File
    eWRIMDSTATICFILE, // Write Immediate Static File
    eRDDRMGROUP, // Read DRM Counters Group
    eWRDRMGROUP, // Write DRM Counters Group
    eWRIMDDRMGROUP, // Write Immediate DRM Counters Group
    ePERIODLOGSUM); // Sum Period logs

  // Read/Write CHS Segment (Action code 12)

  eAC_RDWRCHS = ( //
    eRDCHS_WITHRELO = 1, //
    eWRCHS_WITHRELO, //
    eRVCHS, //
    eRepWRCHS, //
    eRDCHS_WITHOUTRELO, //
    eWRCHS_WITHOUTRELO, //
    eRDCHS_RAW, //
    eWRCHS_RAW, //
    eVCHS_LOSSY, //
    eWRCHS_PATR); //

  // Get Drive Data Tables (Action code 13)

  eAC_GETDRVTBL = ( //
    eDrvTbl = 1, // 1: Drive Physical Parameters
    eTAATbl, // 2: TAA Table
    eWrSenseTbl, // 3: Write Sensitivity Table
    eRROTbl, // 4: RRO Table
    eZoneTbl, // 5: LBA Zone Table
    eWedZoneTbl, // 6: Wedge ZOne Table
    eDrvRdyTbl, // 7: Drive Ready Table
    eMRRTbl, // 8: MR Resistance Table
    eVSCTbl, // 9: VSC Implementation Vector
    eCRTbl, // 10: Cache Relo Table
    eSkLatTbl, // 11: Seek Latency Table
    eMmsCritParm, // 12: MMS Critical Parms
    ePhysZoneTbl, // 13: Physical Zone Table - VDT only
    eDummy, // 14: dummy for now
    eMmsDFHParm, // 15: MMS DFH Parms
    eMmsDFHLiveParm); // 16: MMS DFH live Parms

  // Feature code for Feature Control Command  (Action code 14)

  eAC_FEATCTRL = ( //
    eClearDRMLog = 1, // 1
    eClearFactoryFile, // 2
    eInitODTAAWS, // 3
    eInitRRO, // 4
    eSetDRM, // 5
    eCalibDrvTemp, // 6
    eRdChannelCourseTune, // 7
    eWearLeveling, // 8
    eSetOLIdleTime, // 9
    eSetOLSpinTime, // 10
    eClrDebugstop, // 11
    eDRMPeriodLogShift, // 12
    eDRMFlush, // 13
    eSmartBackDoor, // 14
    eBackgroundActivity, // 15
    eDtempSampling, // 16
    eMeasureMRResistance, // 17
    ePSTMode, // 18
    eFlashCode, // 19 DX only
    eSetDebugStop, // 20
    eClrDRMSection, // 21
    eDestrokeUpstroke, // 22
    eDriveFormat, // 23
    eSmartTestMode, // 24
    eStdbyTmr10MinMinimum, // 25
    eSeekLatencyData, // 26
    eEmulatorBreak, // 27
    eAutoFlushTimer, // 28
    eEnableExtLBA, // 29
    eIdle1Time, // 30
    eIdle2Time, // 31
    eIdle3Time, // 32
    ePowerManagementState, // 33
    eMINUS4_CYL_EMUL, // 34
    eAdvancedPowerManagementLevel, // 35
    eTestID, // 36
    eSmartAttributeIdNewValue, // 37
    eSmartAttributeIdSetMask, // 38
    eSmartAttributeIdClrMask, // 39
    eSscState, // 40
    eSataStaggeredSpinupState, // 41
    eSataInterfaceSpeedState, // 42
    ePowerUpInStandby, // 43
    eServoFlexBiasFilter, // 44
    eOscillatorState, // 45
    eDFHCalibration, // 46
    ePhyConfiguration, // 47
    eSmartSetAttrStatus, // 48 - Set Smart Attribute Status flags
    eSmartSetAttrThreshold, // 49 - Set Smart Attribute Threshold value
    eCFConfigSettings, // 50
    eAltComresetHandler, // 51 - Configure state of alternate comreset handler.
    eDummy52, //
    eDummy53, //
    eDummy54, //
    eDummy55, //
    eDummy56, //
    eDummy57, //
    eSetAggressiveAOLTime); // 58 - Set Aggressive AOL Timer

  // Enums for Feature Control Clear DebugStop

  eClearDebugStop = ( //
    eClrDBSram = 1, // Clear debug stop in ram
    eClrDBSall); // clear debug stop both in memory and file 0x2d

  // Function code for Read/Reset Error Rate Table (Action code 15)

  eAC_ERATETBL = ( //
    eResetErate = 1, //
    eGetErate, //
    eGetResetErate); //

  // Settle Mode used by Logical Seek Command (Action code 18/35)

  eAC_VIRTSK_LOGSK = ( //
    eSK_RDLMT = 0, // Settle on trk using servo read limits and wait for sk complete.
    eSK_WRLMT, // Settle on trk using servo write limits and wait for sk complete.
    eSK_RDLMT_NSC, // Settle on trk using servo read limits and do not wait for sk complete.
    eSK_WRLMT_NSC, // Settle on trk using servo write limits and do not wait for sk complete.
    eSK_RDLMT_NxTrks, //
    eSK_WRLMT_NxTrks, //
    eSK_RDASYNC, // mode 6
    eSK_WRASYNC, // mode 7
    eSK_SRDLMT,  // mode 8, settle on track using servo read limit and wait for sk complete
    eSK_SWRLMT); // mode 9, settle on track using servo write limit and wait for sk complete


  // Function code used by Read/Write Memory Command (Action Code 19)

  eAC_RDWRMEM = ( //
    eRDMEM = 1, // Read memory address
    eWRMEM); // Write memory address

  // Function code for read/write buffer ext (Action code 20)

  eAC_RDWRBUFEXT = ( //
    eRBX_CACHE = 1, //
    eWBX_CACHE, //
    eRBX_DRAM, //
    eWBX_DRAM, //
    eRBX_TRANSOVL, //
    eWBX_TRANSOVL, //
    eRBX_PERMOVL, //
    eWBX_PERMOVL, //
    eRBX_PST, //
    eWBX_PST, //
    eRBX_PST_DATA, //
    eWBX_PST_DATA, //
    eWBX_SOVL, //
    eWBX_COVL); //

  // Servo Control Functions (Action code 23)

  eAC_SERVOCTRL = ( //
    ServoSpinUp = 1, // Servo Spinup
    ServoSpinDn, // Servo Spin Down
    ServoActInit, // Servo actuator Init
    ServoCal, // Servo Calibration
    ServoPark, // Servo Park
    ServoTrace, // Servo Trace feature
    ServoTableId); // Servo Return Table Data

  // Function code for Format Unit/Format Reserved (Action code 24)

  eAC_FMTUNIT = ( //
    eFMTRESERVED = 1, // format reserved
    eFMTUNIT, // Format Unit
    eMERGPG, // Merge P & G List
    eCLRGLIST, // Clear Glist & Relolist
    eREMERGPG); // ReMerge P & G List


  // Function code for Execute File command (Action code 25)

  eAC_ExecFile = ( //
    eDnloadExec = 1, //
    eExecFileInMemory, //
    eExecPST, //
    eExecCaptive, //
    eInitDownloadCodePTM); //

  // Function code for Initiate Power On Reset (Action code 28)

  e_AC_RESET = ( //
    eIMMEDPOR = 1, // Reset immediate
    eSPINDOWNPOR, // Spindown and reset
    eWARMSTART); // Soft Reset

  // Function code used by Read/Write/Erase Flash command (Action code 36)

  e_AC_RWERASEFLASH = ( //
    eRdFlash = 1, // Read Flash
    eWrFlash, // Write Flash
    eEraseFlash); // Erase Flash

  // Function code for File system manager command (Action code 40)

  eAC_FileMgr = ( //
    eValidateFileSystem = 1, //
    eDefragment, //
    eListdirectory, //
    eexpendRSVD); //

  // Read/Write/Verify Reseved LBA command (Action code 50)

  e_AC_RDWRRSVDLBA = ( //
    eRDRLBA = 1, // Read Reserved LBA
    eWRRLBA, // Write Reserved LBA
    eVfyRLBA); // Verify Reserved LBA

  // Function code for Drive Configuration command (Action code 55)

  eAC_DriveConfig = ( //
    eSetProcess1Mode = 1, //
    eSetUserKeyMode, //
    eSetUserPayloadMode, //
    eGetMode, //
    eVerify, //
    eGetVerInfo, //
    eSetRev, //
    eGetFeatKey, //
    eGetAC55Detail, //
    eLoadDataFile, //
    eGetUserKeyMode, //
    eSetProcess2Mode); //

  // Enums for Feature Control Alternate COMRESET

  eAltComreset = ( //
    eDisAltComreset = 1, // disable the alternate comreset method
    eEnAltComreset); // enable the alternate comreset method

  eFileHeaderType = ( //
    eHdrGeneric = 1, //
    eHdrPST, //
    eHdrOVerlay, //
    eHdrFlash); //

  // ======================= BYTES MAPS ==============================

  // -------------------------------------------------
  // Byte Map definition for the 48 Byte fileheader.
  // -------------------------------------------------

type
  // VSC Status Byte Map------------------
  VSC_Status = packed record
    Ver: USHORT; // 2       // 000-001 Format Version
    Minor: UCHAR; // 1       // 002     VSC Implementation Version, Minor
    Major: UCHAR; // 1       // 003     VSC Implementation Version, Minor
    MaxActCode: USHORT; // 2       // 004-005 Max Action Code supported
    LastCmdType: UCHAR; // 1       // 006     Last Cmd Type
    LastCmd: UCHAR; // 1       // 007     Last Cmd REgister value
    LastActCode: USHORT; // 2       // 008-009 Last Action Code
    LastFeat: USHORT; // 2       // 010-011 last Feature register value
    LastSC: USHORT; // 2       // 012-013 last sector count register value
    LastSN: USHORT; // 2       // 014-015 last sector number register value
    LastCyl: USHORT; // 2       // 016-017 last cylinder (Ext. LBA Mid)
    LastLBAHigh: USHORT; // 2       // 018-019 MS 16 bits (Extended commands only)
    DevCtrl: UCHAR; // 1       // 020     Device control register
    DevHd: UCHAR; // 1       // 021     Device/head register
    LastResp: USHORT; // 2       // 022-023 MSB=Task file reg, LSB=Task file Error Reg
    ExtErr: USHORT; // 2       // 024-025 Extended error status of last ATA or VSC Command
    VSCTXCntLo: ULONG; // 4       // 026-029 No. of sector pending to be xferred to/from drive (Total=6 bytes)
    VSCTXCntHi: USHORT; // 2       // 030-031 VSCTxCnt remaining 2 bytes
    TFData: USHORT; // 2       // 032-033 task file data of last vsc command
    StatusFlg1: ULONG; // 4       // 034-037 Status Flag 1
    StatusFlg2: ULONG; // 4       // 038-041 Currently Not Used
    StatusFlg3: ULONG; // 4       // 042-045 Currently Not Used
    checkpoint: UCHAR; // 1       // 046     current DST or Offline activity
    APMLevel: UCHAR; // 1        // 047     APM Level
    SecondaryEcode: USHORT; // 2       // 048-049 Secondary Error Code
    initrCode: ULONG; // 4       // 050-053 Initiator Code
    CurState: UCHAR; // 1       // 054     Current State
    CurState2: UCHAR; // 1       // 055     Current State 2
    CurLBA: array[0..5] of UCHAR; // 1       // 056     LBA of last LBA accessed with or w/o error (total=6 bytes)
    // pad              //5       // 057-061 remaining bytes of CurLBA
    CurTrkOft: USHORT; // 2       // 062-063 Current track offset
    CurVirCyl: ULONG; // 4       // 064-067 virtual cylinder of current active track
    CurVirHd: UCHAR; // 1       // 068     virtual head of current active track
    CurZone: UCHAR; // 1       // 069     Current Data Zone
    CurWedgeStart: USHORT; // 2       // 070-071 No. of counts from servo to start wedge (TOC or Data bits for SOC)
    CurWedgeSize: USHORT; // 2       // 072-073 Size in bytes
    DTemp: UCHAR; // 1       // 074     Current Drive Temperature value
    JTemp: UCHAR; // 1       // 075     Current Junction Temperature value
    CTemp: USHORT; // 2       // 076-077 Current Coil Temperature value
    SvStatus: USHORT; // 2       // 078-079 servo status
    SvGpErr: USHORT; // 2       // 080-081 servo group error
    pad82: USHORT; // 2       // 082-083
    SeedCyl: array[0..2] of UCHAR; // 1       // 084     ECC seed cyl of last read
    // pad              //2       // 085-086 remaining 2 bytes of seed Cyl
    SeedHead: UCHAR; // 1       // 087     ECC seed head of last read
    SeedSector: USHORT; // 2       // 088-089 ECC seed sector of last read
    sectorSkew: USHORT; // 2       // 090-091 sector skew for current track
    wedgeSkew: USHORT; // 2       // 092-093 wedge skew for current track
    pad94: USHORT; // 2       // 094-095 reserved
    DBSCode: ULONG; // 4       // 096-099 Debug stop code
    DBSPara: ULONG; // 4       // 100-103 Debug stop parameter
    BGProgress: USHORT; // 2       // 104-105 Background process indicator
    FTemp: UCHAR; // 1       // 106     Ftemp sensor temperature
    Rsvd1: UCHAR; // 1       // 107     Reserved
    WrFaultLBALow: ULONG; // 4       // 108-111 Write Fault LBA low (bits 0-31)
    WrFaultLBAHigh: USHORT; // 2       // 112-113 Write Fault LBA high (bits 32-47)
    CurCylOftSvo: USHORT; // 2     // 114-115 Current Servo track Offset.  Same as CurTrkOft in Servo unit
    CurSvoCyl: ULONG; // 4     // 116-119 Current Servo Cyl of currently active Servo track
    CurSvoOft: USHORT; // 2     // 120-121 Track offset of currently active Servo track
    ConnectSpeed: UCHAR; // 1         // 122     Current Host Connection speed
    pad123: array[0..276] of UCHAR; // 277     // 123-399 reserved
    PTMId: USHORT; // 2       // 400-401 file id of PTM currently executing
    TestId: USHORT; // 2       // 402-403 test id of the PTM currently executing
    ActionCode: USHORT; // 2       // 404-405 action code of VSC currently executing
    ExtErrCode: USHORT; // 2       // 406-407 extended error code of last VSC command executed by PST
    ActionStatus: ULONG; // 4       // 408-411 current status information specific to the AST module
    pad412: array[0..19] of UCHAR; // 20      // 412-431 remaining bytes for actionstatus
    pad432: array[0..79] of UCHAR; // 80      // 432-511 remaining bytes till 512
  end;

  FileHeader48Byte = packed record
    file_header_version: ULONG; // 4     // [Bytes 00:03] Hardcoded to 0x4c594f52 for FM_HEADER_II
    file_header_type: UCHAR; // 1     // [Byte 04] 1 for generic, 2 for PST, 3 for Overlay, 4 for flash
    reserved5: UCHAR; // 1     // [Byte 05] Padding
    file_header_size: USHORT; // 2     // [Bytes 06:07] Offset to data in File, skip over the file header.
    file_id: USHORT; // 2     // [Bytes 08:09] 16 bit file id.
    file_size: USHORT; // 2     // [Bytes 10:11] Size in sectors (rounded up).
    checksum: ULONG; // 4     // [Bytes 12:15] 32 bit checksum.
    version: array[0..7] of UCHAR; // 4     // [Bytes 16:23] version 8 chars including NULL
    // PAD                          //4
    month: UCHAR; // 1     // [Byte 24] month in hex.
    day: UCHAR; // 1     // [Byte 25] day in hex
    year: UCHAR; // 1     // [Byte 26] 2 digit year in hex.
    reserved27: UCHAR; // 1     // [Byte 27] pad
    description_string: USHORT; // 2     // [Bytes 28:29] Offset in file to description string.
    FM_file_specific: array[0..2] of ULONG; // 4     // [Bytes 30:41] Bytes 30,31 = Build ID to match against Flash Code.
    // PAD                          //14
    reserved42: array[0..5] of UCHAR;
  end;

  FM_file_specific = packed record
    BuildID: USHORT;
    reserved2: array[0..9] of UCHAR;
  end;

  ConfigSectorHdr48 = packed record
    FileHdr: FileHeader48Byte;
    NUM_CONFIG_SECTIONS: USHORT; // 2
    SERIAL_NUMBER_OFFSET: USHORT; // 2
    SERIAL_NUMBER_SIZE: USHORT; // 2
    CAPACITY_CONFIG_OFFSET: USHORT; // 2
    CAPACITY_CONFIG_SIZE: USHORT; // 2
    FEATURE_CONTROL_OFFSET: USHORT; // 2
    FEATURE_CONTROL_SIZE: USHORT; // 2
    CACHE_CONFIG_2MB_OFFSET: USHORT; // 2
    CACHE_CONFIG_2MB_SIZE: USHORT; // 2
    MRM_RPO_CONFIG_2MB_OFFSET: USHORT; // 2
    MRM_RPO_CONFIG_2MB_SIZE: USHORT; // 2
    RSC_CONFIG_2MB_OFFSET: USHORT; // 2
    RSC_CONFIG_2MB_SIZE: USHORT; // 2
    DEFECT_MANAGEMENT_OFFSET: USHORT; // 2
    DEFECT_MANAGEMENT_SIZE: USHORT; // 2
    DISK_CONFIG_OFFSET: USHORT; // 2
    DISK_CONFIG_SIZE: USHORT; // 2
    SERVO_CONFIG_OFFSET: USHORT; // 2
    SERVO_CONFIG_SIZE: USHORT; // 2
    READ_CHANNEL_CONFIG_OFFSET: USHORT; // 2
    READ_CHANNEL_CONFIG_SIZE: USHORT; // 2
    SMART_CONFIG_OFFSET: USHORT; // 2
    SMART_CONFIG_SIZE: USHORT; // 2
    VSC_CONFIG_OFFSET: USHORT; // 2
    VSC_CONFIG_SIZE: USHORT; // 2
    BACKGROUND_CONFIG_OFFSET: USHORT; // 2
    BACKGROUND_CONFIG_SIZE: USHORT; // 2
    CDD_CONFIG_OFFSET: USHORT; // 2
    CDD_CONFIG_SIZE: USHORT; // 2
    ERROR_RECOVERY_OFFSET: USHORT; // 2
    ERROR_RECOVERY_SIZE: USHORT; // 2
    FORMAT_OFFSET: USHORT; // 2
    FORMAT_SIZE: USHORT; // 2
    MODEL_STRING_OFFSET: USHORT; // 2
    MODEL_STRING_SIZE: USHORT; // 2
    DRIVE_LABEL_OFFSET: USHORT; // 2
    DRIVE_LABEL_SIZE: USHORT; // 2
    PASSWORD_OFFSET: USHORT; // 2
    PASSWORD_SIZE: USHORT; // 2
    CACHE_CONFIG_8MB_OFFSET: USHORT; // 2
    CACHE_CONFIG_8MB_SIZE: USHORT; // 2
    MRM_RPO_CONFIG_8MB_OFFSET: USHORT; // 2
    MRM_RPO_CONFIG_8MB_SIZE: USHORT; // 2
    RSC_CONFIG_8MB_OFFSET: USHORT; // 2
    RSC_CONFIG_8MB_SIZE: USHORT; // 2
    SATA_SECTION_OFFSET: USHORT; // 2
    SATA_SECTION_SIZE: USHORT; // 2
  end;

  // -------------------------------------------------------------------------
  // VDT Sector Physical Zone Table (For Action Code 13, Get Drive Data Table)
  // -------------------------------------------------------------------------
  SectPhysZoneTblHdr = record
    Ver: USHORT; // 2
    numzones: UCHAR; // 1
    currhead: UCHAR; // 1
    rsvdDataCyls: USHORT; // 2
    maxVirtCyls: ULONG; // 4
    rsvdSvoCyls: USHORT; // 2
    maxSvoCyls: ULONG; // 4
    Pad: array[0..33] of UCHAR; // 34
  end;

  SectPhysZoneTbl = record
    FirstCyl_Zn: ULONG; // 4
    LastCyl_Zn: ULONG; // 4
    FirstServoCyl: ULONG; // 4
    LastServoCyl: ULONG; // 4
    SPTHd0_Zn: USHORT; // 2
  end;

  // ---------------------------------------------------------
  // Byte Map definition for the PST file type FHII.
  // ---------------------------------------------------------
  PSTHdr = record
    StartAddress: ULONG; // 4    // [Bytes 30:33] Start address
    LoadAddress: ULONG; // 4    // [Bytes 34:37] Load Address
    BytesInFile: ULONG; // 4    // [Bytes 38:41] Bytes in File
  end;

  // --------------------------------------------------
  // Bytemap definition for the LBA Dirsector Entry
  // --------------------------------------------------
  LBADirEntry = record
    next_entry_offset: UCHAR; // 1;    // Number of bytes to next File ID.  Zero to indicate last entry.
    num_file_copy: UCHAR; // 1;    // Number of copies this file has in the reserved area
    file_id: USHORT; // 2;    // If FM_FILE_HEADER_II is enabled, now 2 bytes
    // including this copy and any in flash
    file_size: USHORT; // 2;    // size of file in Sector
    file_attr: ULONG; // 4;   //File attribute.
  end;
  // copy_num0           //4;    //Note:  Can use command GetCopyNum or access directly using copy_num# etc.
  // copy_num1           //4;
  // copy_num2           //4;
  // copy_num3           //4;
  // copy_num4           //4;
  // copy_num5           //4;
  // copy_num6           //4;
  // copy_num7           //4;

  // -----------------------------------------------------------------
  // Byte Map definition for the Packet FileList Entry fileheaderII
  // -----------------------------------------------------------------

  FileListHdr = record
    nextEntryOffset: UCHAR; // 1     // [Byte 01] Offset to next BmFileListHdr
    numCopies: UCHAR; // 1     // [Byte 02]
    file_id: USHORT; // 2     // [Bytes 03:04] 16 bit file id.
    file_size: USHORT; // 2     // [Bytes 05:06] Size in sectors (rounded up).
    attr: ULONG; // 4     // [Bytes 07:10] This file's attribute
    address: ULONG;  // 4     // [Bytes 11:14] Starting address of file pointed to by this entry
    versionMaj: ULONG; // 4     // [Bytes 15:18] version 8 chars including NULL
    versionMin: ULONG; // 4     // [Bytes 19:22]
  end;

  // BYTE Maps for VSC Key SEctor
  pKey_Sect = ^Key_Sect;

  Key_Sect = packed record
    ActionCode: USHORT; // 2
    parm1: USHORT; // 2
    parm2: USHORT; // 2
    parm3: USHORT; // 2
    parm4: USHORT; // 2
    parm5: USHORT; // 2
    parm6: USHORT; // 2
    parm7: USHORT; // 2
    parm8: USHORT; // 2
    parm9: USHORT; // 2
    parm10: USHORT; // 2
    parm11: USHORT; // 2
    parm12: USHORT; // 2
    parm13: USHORT; // 2
    parm14: USHORT; // 2
    parm15: USHORT; // 2
  end;

  // Special BYTE Maps For VSC Execute File Key Sector
  pXFile_Key_Sect = ^XFile_Key_Sect;

  XFile_Key_Sect = packed record
    ActionCode: USHORT; // 2
    FuncCode: USHORT; // 2
    FileID: USHORT; // 2
    TestId: USHORT; // 2
    Testparm1: ULONG; // 4
    Testparm2: ULONG; // 4
    Testparm3: ULONG; // 4
    Testparm4: ULONG; // 4
    Testparm5: ULONG; // 4
    Testparm6: ULONG; // 4
    Testparm7: ULONG; // 4
    Testparm8: ULONG; // 4
    Testparm9: ULONG; // 4
    Testparm10: ULONG; // 4
    Testparm11: ULONG; // 4
    Testparm12: ULONG; // 4
    Testparm13: ULONG; // 4
    Testparm14: ULONG; // 4
    Testparm15: ULONG; // 4
    Testparm16: ULONG; // 4
  end;

var
  KeySector: array[0..511] of Byte;
  SuperBuffer: TBytes;
  KeySect: Key_Sect;
  XFileKeySect: XFile_Key_Sect;
  VSCStatus: VSC_Status;
  ExtErr: Word;

function VscOn(): Boolean;

function VscOff(): Boolean;

function SmartRdLog(blk, Key: Word): Boolean;

function SmartWrLog(blk, Key: Word): Boolean;

function GetVscStatus(): Boolean;

function VscStatusOK(): Boolean;

function GetExtErr(): Word;

function RoylChecksum32(var Buffer: TBytes): DWORD;

function Checksum16(var Buffer: TBytes): Word;

function Checksum8(var Buffer: TBytes): Word;

function ChecksumA(var Buffer: TBytes): Word;

function ReadFileToBuffer(FileName: string; var Buffer: TBytes): Boolean; overload;

function ReadFileToBuffer(FileName: string): Boolean; overload;

function SaveFileFromBuffer(FileName: string; var Buffer: TBytes): Boolean; overload;

function SaveFileFromBuffer(FileName: string): Boolean; overload;

function WriteKeySector(Buffer: TBytes; TimeOut: Cardinal = 5000): Word; overload;

function WriteKeySector(TimeOut: Cardinal = 5000): Word; overload;

function VSCMD(ac_code: Word = 0; p1: Word = 0; p2: Word = 0; p3: Word = 0; p4: Word = 0; p5: Word = 0; p6: Word = 0; p7: Word = 0; p8: Word = 0; p9: Word = 0): Word;

function XFile(FileID, TestId: Word; p1: DWORD = 0; p2: DWORD = 0; p3: DWORD = 0; p4: DWORD = 0; p5: DWORD = 0; p6: DWORD = 0; p7: DWORD = 0; p8: DWORD = 0; p9: DWORD = 0; p10: DWORD = 0; p11: DWORD = 0; p12: DWORD = 0; p13: DWORD = 0; p14: DWORD = 0; p15: DWORD = 0; p16: DWORD = 0): Boolean;

function IReset(): Boolean;

function SpReset(): Boolean;

function ReStart(): Boolean;

function sv_spinup(): Boolean;

function sv_spindn(): Boolean;

function ReadDataToBuffer(var Buffer: TBytes): Boolean; overload;

function ReadDataToBuffer(var Buffer: TBytes; var Offset: Integer; Count: Integer): Boolean; overload;

function WriteDataFromBuffer(var Buffer: TBytes): Boolean; overload;

function WriteDataFromBuffer(var Buffer: TBytes; var Offset: Integer; Count: Integer): Boolean; overload;

function XfrRdData(): Boolean;

function XfrWrData(): Boolean;

function chkresf(FileID: Word): Word;

function rdfl(FileID: Word; Count: Word = 0): Word;

function wrfl(FileID, Count: Word): Word;

function prdfl(FileID, Offset, Count: Word): Word;

function pwrfl(FileID, Offset, Count: Word): Word;

implementation

uses
  uTaskFile;

function VscOn(): Boolean;
begin
  Result := TaskFile.ATACMD($45, $00, $00, $44, $57, TaskFile.DeviceReg, $80);
  if Result then
    Result := TaskFile.DRDY or TaskFile.DSC;
end;

function VscOff(): Boolean;
begin
  Result := TaskFile.ATACMD($44, $00, $00, $44, $57, TaskFile.DeviceReg, $80);
  if Result then
    Result := TaskFile.DRDY or TaskFile.DSC;
end;

function SmartRdLog(blk: Word; Key: Word): Boolean;
begin
  Result := TaskFile.ATACMD($D5, blk, Key, $4F, $C2, TaskFile.DeviceReg, $B0);
  if Result then
    Result := TaskFile.DRQ;
end;

function SmartWrLog(blk: Word; Key: Word): Boolean;
begin
  Result := TaskFile.ATACMD($D6, blk, Key, $4F, $C2, TaskFile.DeviceReg, $B0);
  if Result then
    Result := TaskFile.DRQ;
end;

function GetVscStatus(): Boolean;
var
  Buffer: TBytes;
begin
  Result := False;
  if not SmartRdLog(1, SMART_CTRLKEY) then
    Exit;
  SetLength(Buffer, 512);
  try
    if not TaskFile.ReadDataToBuffer(Buffer) then
      Exit;
    ExtErr := PWORD(@Buffer[24])^;
    ZeroMemory(@VSCStatus, SizeOf(VSCStatus));
    CopyMemory(@VSCStatus, @Buffer[0], SizeOf(VSCStatus));
    Result := True
  finally
    SetLength(Buffer, 0);
  end;
end;

function VscStatusOK(): Boolean;
begin
  Result := False;
  if not GetVscStatus then
    Exit;
  Result := (ExtErr = $0) or (ExtErr = $FFFF) or (ExtErr = $C409);
end;

function GetExtErr(): Word;
begin
  if TaskFile.ERR then
    ExtErr := TaskFile.InPortB(TaskFile.AtaRegister.SC.PortAddr) + //
      TaskFile.InPortB(TaskFile.AtaRegister.LBAlo.PortAddr) shl 8
  else
    GetVscStatus;
  if ExtErr = $FFFF then
    ExtErr := 0;
  Result := ExtErr;
end;

function RoylChecksum32(var Buffer: TBytes): DWORD;
var
  i, j: Integer;
  CRC: Int64;
begin
  CRC := 0;
  j := Length(Buffer) div 4 - 1;
  for i := 0 to j do
  begin
    if i = 3 then
      Continue;
    CRC := CRC - PDWORD(@Buffer[i * 4])^;
  end;
  CRC := CRC and $FFFFFFFF;
  Buffer[12] := CRC;
  Buffer[13] := CRC shr 8;
  Buffer[14] := CRC shr 16;
  Buffer[15] := CRC shr 24;
  Result := CRC;
end;

function Checksum16(var Buffer: TBytes): Word;
var
  i, j: Integer;
  CRC: Int64;
begin
  CRC := 0;
  j := Length(Buffer) div 2 - 1;
  for i := 0 to j do
  begin
    CRC := CRC - PWORD(@Buffer[i * 2])^;
  end;
  CRC := CRC and $FFFF;
  Result := CRC;
end;

function Checksum8(var Buffer: TBytes): Word;
var
  i, j: Integer;
  CRC: Int64;
begin
  CRC := 0;
  j := Length(Buffer) - 1;
  for i := 0 to j do
  begin
    CRC := CRC - Buffer[i];
  end;
  CRC := CRC and $FFFF;
  Result := CRC;
end;

function ChecksumA(var Buffer: TBytes): Word;
var
  CRC: Int64;
  FileHdSz, DataSZ: Byte;
  BitmapData: TBytes;
begin
  CRC := 0;
  if (PDWORD(@Buffer[0])^ = $4C594F52) and (PWORD(@Buffer[8])^ = $A) then
  begin
    FileHdSz := Buffer[6];
    DataSZ := Buffer[FileHdSz];
    SetLength(BitmapData, DataSZ);
    try
      Buffer[$3C] := 0;
      Buffer[$3D] := 0;
      CopyMemory(@BitmapData[0], @Buffer[FileHdSz], DataSZ);
      CRC := Checksum8(BitmapData);
      Buffer[$3C] := CRC;
      Buffer[$3D] := CRC shr 8;
      RoylChecksum32(Buffer);
    finally
      SetLength(BitmapData, 0);
    end;
  end;
  Result := CRC;
end;

function ReadFileToBuffer(FileName: string; var Buffer: TBytes): Boolean;
var
  FileStrem: TFileStream;
  Size, Count: Integer;
begin
  try
    FileStrem := TFileStream.Create(FileName, fmOpenRead or fmShareDenyNone);
    Count := FileStrem.Size;
    SetLength(Buffer, Count);
    try
      Size := FileStrem.Read(Buffer, Count);
    finally
      FileStrem.Destroy;
    end;
    Result := Size = Count;
  except
    Result := False;
  end;
end;

function ReadFileToBuffer(FileName: string): Boolean;
begin
  Result := False;
  if ReadFileToBuffer(FileName, SuperBuffer) then
  begin
    if (PDWORD(@SuperBuffer[0])^ = $4C594F52) and (PWORD(@SuperBuffer[10])^ = Length(SuperBuffer) div 512) then
      RoylChecksum32(SuperBuffer);
    Result := True;
  end;
end;

function SaveFileFromBuffer(FileName: string; var Buffer: TBytes): Boolean;
var
  FileStrem: TFileStream;
  Size: Integer;
begin
  FileStrem := TFileStream.Create(FileName, fmCreate);
  try
    Size := FileStrem.Write(Buffer, Length(Buffer));
  finally
    FileStrem.Destroy;
  end;
  Result := Size = Length(Buffer);
end;

function SaveFileFromBuffer(FileName: string): Boolean;
begin
  if (PDWORD(@SuperBuffer[0])^ = $4C594F52) and (PWORD(@SuperBuffer[10])^ = Length(SuperBuffer) div 512) then
    RoylChecksum32(SuperBuffer);
  Result := SaveFileFromBuffer(FileName, SuperBuffer);
end;

function WriteKeySector(Buffer: TBytes; TimeOut: Cardinal = 5000): Word;
begin
  Result := $EEEE;
  if VscOn = False then
    Exit;
  if SmartWrLog(1, SMART_CTRLKEY) = False then
    Exit;
  TaskFile.WriteDataFromBuffer(Buffer);
  if TaskFile.WaitNotBSY(TimeOut) = False then
    Exit;
  ExtErr := TaskFile.InPortB(TaskFile.AtaRegister.SC.PortAddr) + TaskFile.InPortB(TaskFile.AtaRegister.LBAlo.PortAddr) shl 8;
  Result := ExtErr;
end;

function WriteKeySector(TimeOut: Cardinal = 5000): Word;
var
  Buffer: TBytes;
begin
  SetLength(Buffer, 512);
  try
    CopyMemory(@Buffer[0], @KeySector[0], 512);
    ZeroMemory(@KeySector, 512);
    ZeroMemory(@KeySect, SizeOf(KeySect));
    Result := WriteKeySector(Buffer, TimeOut);
  finally
    SetLength(Buffer, 0);
  end;
end;

function VSCMD(ac_code: Word = 0; p1: Word = 0; p2: Word = 0; p3: Word = 0; p4: Word = 0; p5: Word = 0; p6: Word = 0; p7: Word = 0; p8: Word = 0; p9: Word = 0): Word;
begin
  KeySect.ActionCode := ac_code;
  KeySect.parm1 := p1;
  KeySect.parm2 := p2;
  KeySect.parm3 := p3;
  KeySect.parm4 := p4;
  KeySect.parm5 := p5;
  KeySect.parm6 := p6;
  KeySect.parm7 := p7;
  KeySect.parm8 := p8;
  KeySect.parm9 := p9;

  CopyMemory(@KeySector[0], @KeySect, SizeOf(KeySect));
  Result := WriteKeySector;
end;

function XFile(FileID, TestId: Word; p1: DWORD = 0; p2: DWORD = 0; p3: DWORD = 0; p4: DWORD = 0; p5: DWORD = 0; p6: DWORD = 0; p7: DWORD = 0; p8: DWORD = 0; p9: DWORD = 0; p10: DWORD = 0; p11: DWORD = 0; p12: DWORD = 0; p13: DWORD = 0; p14: DWORD = 0; p15: DWORD = 0; p16: DWORD = 0): Boolean;
begin
  ZeroMemory(@XFileKeySect, SizeOf(XFileKeySect));

  XFileKeySect.ActionCode := AC_ExecFile;
  XFileKeySect.FuncCode := Word(eExecPST);
  XFileKeySect.FileID := FileID;
  XFileKeySect.TestId := TestId;
  XFileKeySect.Testparm1 := p1;
  XFileKeySect.Testparm2 := p2;
  XFileKeySect.Testparm3 := p3;
  XFileKeySect.Testparm4 := p4;
  XFileKeySect.Testparm5 := p5;
  XFileKeySect.Testparm6 := p6;
  XFileKeySect.Testparm7 := p7;
  XFileKeySect.Testparm8 := p8;
  XFileKeySect.Testparm9 := p9;
  XFileKeySect.Testparm10 := p10;
  XFileKeySect.Testparm11 := p11;
  XFileKeySect.Testparm12 := p12;
  XFileKeySect.Testparm13 := p13;
  XFileKeySect.Testparm14 := p14;
  XFileKeySect.Testparm15 := p15;
  XFileKeySect.Testparm16 := p16;

  CopyMemory(@KeySector[0], @XFileKeySect, SizeOf(XFileKeySect));
  Result := WriteKeySector() = 0;
end;

// ----------------------------------------------------------------------------------
// Reset drive immediate (Action Code 28)
// Input:
// Output: Reset the drive.
// ----------------------------------------------------------------------------------
function IReset(): Boolean;
begin
  KeySect.ActionCode := AC_RESET;
  KeySect.parm1 := Word(eIMMEDPOR);

  CopyMemory(@KeySector[0], @KeySect, SizeOf(KeySect));
  Result := WriteKeySector(10000) = 0;
end;

// ----------------------------------------------------------------------------------
// Spin Down and Reset drive(Action Code 28)
// Input:
// Output: Reset the drive.
// ----------------------------------------------------------------------------------
function SpReset(): Boolean;
begin
  KeySect.ActionCode := AC_RESET;
  KeySect.parm1 := Word(eSPINDOWNPOR);

  CopyMemory(@KeySector[0], @KeySect, SizeOf(KeySect));
  Result := WriteKeySector(10000) = 0;
end;

// ----------------------------------------------------------------------------------
// Spin Down and Reset drive(Action Code 28)
// Input:
// Output: Reset the drive.
// ----------------------------------------------------------------------------------
function ReStart(): Boolean;
begin
  KeySect.ActionCode := AC_RESET;
  KeySect.parm1 := Word(eWARMSTART);

  CopyMemory(@KeySector[0], @KeySect, SizeOf(KeySect));
  Result := WriteKeySector(10000) = 0;
end;

// ----------------------------------------------------------------------------------
// SV_SpinUp (Servo SpinUp)
// Input:  none
// output: none
// ----------------------------------------------------------------------------------
function sv_spinup(): Boolean;
begin
  KeySect.ActionCode := AC_SERVOCTRL;
  KeySect.parm1 := Word(ServoSpinUp);

  CopyMemory(@KeySector[0], @KeySect, SizeOf(KeySect));
  Result := WriteKeySector(10000) = 0;
end;

// ----------------------------------------------------------------------------------
// SV_SpinDn (Servo SpinDn)
// Input:  none
// output: none
// ----------------------------------------------------------------------------------
function sv_spindn(): Boolean;
begin
  KeySect.ActionCode := AC_SERVOCTRL;
  KeySect.parm1 := Word(ServoSpinDn);

  CopyMemory(@KeySector[0], @KeySect, SizeOf(KeySect));
  Result := WriteKeySector() = 0;
end;

function ReadDataToBuffer(var Buffer: TBytes): Boolean;
var
  blk, Offset, Count: Integer;
  dwData: DWORD;
  rSTAT: Byte;
begin
  Result := False;
  Offset := 0;
  Count := Length(Buffer) div 512;
  while (Count > 0) do
  begin
    if (Count > 256) then
      blk := 256
    else
      blk := Count;
    if not SmartRdLog(blk, SMART_DATAKEY) then
      Exit;
    rSTAT := TaskFile.InPortB(TaskFile.AltPort);
    while TaskFile.DRQ(rSTAT) do
    begin
      if TaskFile.DRQ(rSTAT) then
      begin
        dwData := TaskFile.InPortDW(TaskFile.BasePort);
        Buffer[Offset] := dwData;
        Buffer[Offset + 1] := dwData shr 8;
        Buffer[Offset + 2] := dwData shr 16;
        Buffer[Offset + 3] := dwData shr 24;
        Offset := Offset + 4;
      end;
      rSTAT := TaskFile.InPortB(TaskFile.AltPort);
      if TaskFile.ERR(rSTAT) then
        Exit;
      while TaskFile.BSY(rSTAT) do
        rSTAT := TaskFile.InPortB(TaskFile.AltPort);
    end;
    Count := Count - 256;
  end;
  Result := Count <= 0;
end;

function ReadDataToBuffer(var Buffer: TBytes; var Offset: Integer; Count: Integer): Boolean;
var
  blk: Integer;
  dwData: DWORD;
  rSTAT: Byte;
begin
  Result := False;
  while (Count > 0) do
  begin
    if (Count > 256) then
      blk := 256
    else
      blk := Count;
    if not SmartRdLog(blk, SMART_DATAKEY) then
      Exit;
    rSTAT := TaskFile.InPortB(TaskFile.AltPort);
    while TaskFile.DRQ(rSTAT) do
    begin
      if TaskFile.DRQ(rSTAT) then
      begin
        dwData := TaskFile.InPortDW(TaskFile.BasePort);
        Buffer[Offset] := dwData;
        Buffer[Offset + 1] := dwData shr 8;
        Buffer[Offset + 2] := dwData shr 16;
        Buffer[Offset + 3] := dwData shr 24;
        Offset := Offset + 4;
      end;
      rSTAT := TaskFile.InPortB(TaskFile.AltPort);
      if TaskFile.ERR(rSTAT) then
        Exit;
      while TaskFile.BSY(rSTAT) do
        rSTAT := TaskFile.InPortB(TaskFile.AltPort);
    end;
    Count := Count - 256;
  end;
  Result := Count <= 0;
end;

function WriteDataFromBuffer(var Buffer: TBytes): Boolean;
var
  blk, Offset, Count: Integer;
{$IFDEF WIN64}
  dwData: DWORD;
{$ELSE}
  dwData: Word;
{$ENDIF}
  rSTAT: Byte;
begin
  Result := False;
  Offset := 0;
  Count := Length(Buffer) div 512;
  while (Count > 0) do
  begin
    if (Count > 256) then
      blk := 256
    else
      blk := Count;
    if not SmartWrLog(blk, SMART_DATAKEY) then
      Exit;
    rSTAT := TaskFile.InPortB(TaskFile.AltPort);
    while TaskFile.DRQ(rSTAT) do
    begin
      if TaskFile.DRQ(rSTAT) then
      begin
{$IFDEF WIN64}
        dwData := PDWORD(@Buffer[Offset])^;
        TaskFile.OutPortDW(TaskFile.BasePort, dwData);
        Offset := Offset + 4;
{$ELSE}
        dwData := PWORD(@Buffer[Offset])^;
        TaskFile.OutPortW(TaskFile.BasePort, dwData);
        Offset := Offset + 2;
{$ENDIF}
      end;
      rSTAT := TaskFile.InPortB(TaskFile.AltPort);
      if TaskFile.ERR(rSTAT) then
        Exit;
      if not TaskFile.WaitNotBSY(3000) then
        Exit;
    end;
    Count := Count - 256;
  end;
  Result := Count <= 0;
end;

function WriteDataFromBuffer(var Buffer: TBytes; var Offset: Integer; Count: Integer): Boolean;
var
  blk: Integer;
{$IFDEF WIN64}
  dwData: DWORD;
{$ELSE}
  dwData: Word;
{$ENDIF}
  rSTAT: Byte;
begin
  Result := False;
  while (Count > 0) do
  begin
    if (Count > 256) then
      blk := 256
    else
      blk := Count;
    if not SmartWrLog(blk, SMART_DATAKEY) then
      Exit;
    rSTAT := TaskFile.InPortB(TaskFile.AltPort);
    while TaskFile.DRQ(rSTAT) do
    begin
      if TaskFile.DRQ(rSTAT) then
      begin
{$IFDEF WIN64}
        dwData := PDWORD(@Buffer[Offset])^;
        TaskFile.OutPortDW(TaskFile.BasePort, dwData);
        Offset := Offset + 4;
{$ELSE}
        dwData := PWORD(@Buffer[Offset])^;
        TaskFile.OutPortW(TaskFile.BasePort, dwData);
        Offset := Offset + 2;
{$ENDIF}
      end;
      rSTAT := TaskFile.InPortB(TaskFile.AltPort);
      if TaskFile.ERR(rSTAT) then
        Exit;
      if not TaskFile.WaitNotBSY(3000) then
        Exit;
    end;
    Count := Count - 256;
  end;
  Result := Count <= 0;
end;

function XfrRdData(): Boolean;
var
  numSectors: Integer;
begin
  Result := False;
  numSectors := TaskFile.InPortB(TaskFile.BasePort + 4) + TaskFile.InPortB(TaskFile.BasePort + 5) shl 8;
  if numSectors = 0 then
    Exit;
  SetLength(SuperBuffer, numSectors * 512);
  Result := ReadDataToBuffer(SuperBuffer);
end;

function XfrWrData(): Boolean;
var
  numSectors: Integer;
begin
  Result := False;
  numSectors := TaskFile.InPortB(TaskFile.BasePort + 4) + TaskFile.InPortB(TaskFile.BasePort + 5) shl 8;
  if numSectors = 0 then
    Exit;
  Result := WriteDataFromBuffer(SuperBuffer);
end;

function chkresf(FileID: Word): Word;
begin
  KeySect.ActionCode := AC_CHKFILEID;
  KeySect.parm1 := FileID;

  CopyMemory(@KeySector[0], @KeySect, SizeOf(KeySect));
  if (FileID = 4) or (FileID = 5) then
    Result := WriteKeySector(30000)
  else
    Result := WriteKeySector(10000);
end;

function rdfl(FileID: Word; Count: Word = 0): Word;
begin
  if Count = 0 then
  begin
    if rdfl(FileID, 1) = 0 then
      Count := PWORD(@SuperBuffer[$A])^;
  end;
  try
    SetLength(SuperBuffer, Count * 512);
    ExtErr := VSCMD(8, 1, FileID);
    if ExtErr = 0 then
      ReadDataToBuffer(SuperBuffer)
    else if (ExtErr = $3603) or (ExtErr = $3712) then
      ExtErr := prdfl(FileID, 0, Count);
  finally
  end;
  Result := ExtErr;
end;

function wrfl(FileID, Count: Word): Word;
begin
  try
    ExtErr := VSCMD(8, 2, FileID);
    if ExtErr = 0 then
      WriteDataFromBuffer(SuperBuffer)
    else if (ExtErr = $3603) or (ExtErr = $3712) then
      ExtErr := pwrfl(FileID, 0, Count);
  finally
  end;
  Result := ExtErr;
end;

function prdfl(FileID, Offset, Count: Word): Word;
var
  i, j, bfrOffset: Integer;
begin
  try
    SetLength(SuperBuffer, Count * 512);
    i := Count - Offset;
    if i <= 256 then
    begin
      if VSCMD(8, 13, FileID, Offset, 0, 0, 0, Count) = 0 then
        ReadDataToBuffer(SuperBuffer);
    end
    else
    begin
      bfrOffset := 0;
      while i > 0 do
      begin
        if i > 256 then
          j := 256
        else
          j := i;
        if VSCMD(8, 13, FileID, Offset, 0, 0, 0, j) = 0 then
          ReadDataToBuffer(SuperBuffer, bfrOffset, j);
        Offset := Offset + j;
        i := i - 256;
      end;
    end;
  finally
  end;
  Result := ExtErr;
end;

function pwrfl(FileID, Offset, Count: Word): Word;
var
  i, j, bfrOffset: Integer;
begin
  try
    i := Count - Offset;
    if i <= 256 then
    begin
      if VSCMD(8, 14, FileID, Offset, 0, 0, 0, Count) = 0 then
        WriteDataFromBuffer(SuperBuffer);
    end
    else
    begin
      bfrOffset := 0;
      while i > 0 do
      begin
        if i > 256 then
          j := 256
        else
          j := i;
        if VSCMD(8, 14, FileID, Offset, 0, 0, 0, j) = 0 then
          WriteDataFromBuffer(SuperBuffer, bfrOffset, j);
        Offset := Offset + j;
        i := i - 256;
      end;
    end;
  finally
  end;
  Result := ExtErr;
end;

end.

